Full Provisional Code

#include <xc.h> 

#include <stdint.h> 

#include <stdbool.h> 

#define _XTAL_FREQ 4000000 

// ===================================================== 

// CONFIG BITS (PIC18F18877) 

// ===================================================== 

#pragma config FEXTOSC = OFF 

#pragma config RSTOSC = HFINT1 

#pragma config WDTE = OFF 

#pragma config LVP = OFF 

#pragma config MCLRE = ON 

// ===================================================== 

// LED DEFINITIONS 

// ===================================================== 

#define GREEN_LED  LATAbits.LATA0 

#define RED_LED    LATAbits.LATA1 

#define YELLOW_LED LATAbits.LATA2 

#define WHITE_LED  LATAbits.LATA3 

// ===================================================== 

// I2C ADDRESSES 

// ===================================================== 

#define MAX30102_ADDR 0x57 

#define MLX90614_ADDR 0x5A 

// ===================================================== 

// FILTER 

// ===================================================== 

#define FILTER_SIZE 5 

unsigned int ir_buffer[FILTER_SIZE]; 

unsigned char filter_index = 0; 

// ===================================================== 

// HEART RATE VARIABLES 

// ===================================================== 

volatile unsigned int ms_ticks = 0; 

unsigned int last_peak_time = 0; 

unsigned int bpm = 0; 

// ===================================================== 

// FUNCTION PROTOTYPES 

// ===================================================== 

void OSC_Init(void); 

void GPIO_Init(void); 

void I2C_Init(void); 

void TIMER0_Init(void); 

char I2C_Check(unsigned char addr); 

unsigned int Read_IR_Value(void); 

unsigned int Read_Temp_Raw(void); 

unsigned int MovingAverageFilter(unsigned int value); 

bool DetectPeak(unsigned int value); 

float ConvertTemp(unsigned int raw); 

// ===================================================== 

// INTERRUPT (1ms TIMER) 

// ===================================================== 

void __interrupt() ISR(void) 

{ 

    if (INTCONbits.TMR0IF) 

    { 

        INTCONbits.TMR0IF = 0; 

        TMR0 = 100; // reload for ~1ms 

        ms_ticks++; 

    } 

} 

// ===================================================== 

// MAIN 

// ===================================================== 

void main(void) 

{ 

    OSC_Init(); 

    GPIO_Init(); 

    I2C_Init(); 

    TIMER0_Init();  

    GREEN_LED = RED_LED = YELLOW_LED = WHITE_LED = 0;  

    unsigned int ir_raw, ir_filtered; 

    unsigned int temp_raw; 

    float temp_c; 

  

    while(1) 

    { 

        // ---------------- SENSOR CHECK ---------------- 

        char heart_ok = I2C_Check(MAX30102_ADDR); 

        char temp_ok  = I2C_Check(MLX90614_ADDR);  

        // ---------------- HEART RATE ---------------- 

        ir_raw = Read_IR_Value(); 

        ir_filtered = MovingAverageFilter(ir_raw);  

        if (DetectPeak(ir_filtered)) 

        { 

            unsigned int diff = ms_ticks - last_peak_time;  

            if (last_peak_time != 0 && diff > 0) 

            { 

                bpm = 60000 / diff; 

            }  

            last_peak_time = ms_ticks; 

        }  

        // ---------------- TEMPERATURE ---------------- 

        temp_raw = Read_Temp_Raw(); 

        temp_c = ConvertTemp(temp_raw); 

         // ---------------- LED RESET ---------------- 

        GREEN_LED = RED_LED = YELLOW_LED = WHITE_LED = 0; 

         // ---------------- STATE LOGIC ---------------- 

        if (bpm > 85 && temp_c > 38.0) 

        { 

            WHITE_LED = 1; 

        } 

        else 

        { 

            if (bpm <= 85 && temp_c <= 38.0) 

                GREEN_LED = 1; 

             if (bpm > 85) 

                RED_LED = 1; 

             if (temp_c > 38.0) 

                YELLOW_LED = 1; 

        } 

         __delay_ms(50); 

    } 

} 

// ===================================================== 

// OSCILLATOR 

// ===================================================== 

void OSC_Init(void) 

{ 

    OSCCON1 = 0x60; // HFINTOSC 

    OSCFRQ = 0x02;  // 4 MHz 

} 

// ===================================================== 

// GPIO INIT 

// ===================================================== 

void GPIO_Init(void) 

{ 

    TRISA = 0x00; 

    ANSELA = 0x00; // disable analog 

    GREEN_LED = 0; 

    RED_LED = 0; 

    YELLOW_LED = 0; 

    WHITE_LED = 0; 

} 

// ===================================================== 

// TIMER0 INIT (1ms tick) 

// ===================================================== 

void TIMER0_Init(void) 

{ 

    T0CON0 = 0x90; // 16-bit, enabled 

    T0CON1 = 0x40; // Fosc/4, prescaler 1:1 

    TMR0 = 100; 

    INTCONbits.TMR0IF = 0; 

    INTCONbits.TMR0IE = 1; 

    INTCONbits.GIE = 1; 

} 

// ===================================================== 

// I2C INIT (MSSP1) 

// ===================================================== 

void I2C_Init(void) 

{ 

    TRISC3 = 1; 

    TRISC4 = 1; 

    // PPS (IMPORTANT for PIC18F18877) 

    SSP1CLKPPS = 0x13; // RC3 

    SSP1DATPPS = 0x14; // RC4 

    RC3PPS = 0x0F;     // SCL 

    RC4PPS = 0x10;     // SDA 

    SSP1CON1 = 0x28; 

    SSP1CON2 = 0x00; 

    SSP1ADD = 9; 

    SSP1STAT = 0x00; 

} 

// ===================================================== 

// I2C CHECK DEVICE 

// ===================================================== 

char I2C_Check(unsigned char addr) 

{ 

    SSP1CON2bits.SEN = 1; 

    while (SSP1CON2bits.SEN); 

    SSP1BUF = (addr << 1); 

    while (!PIR1bits.SSP1IF); 

    PIR1bits.SSP1IF = 0; 

    char ack = !SSP1CON2bits.ACKSTAT; 

    SSP1CON2bits.PEN = 1; 

    while (SSP1CON2bits.PEN); 

    return ack; 

} 

// ===================================================== 

// MOVING AVERAGE FILTER 

// ===================================================== 

unsigned int MovingAverageFilter(unsigned int value) 

{ 

    ir_buffer[filter_index] = value; 

    filter_index = (filter_index + 1) % FILTER_SIZE; 

    unsigned long sum = 0; 

    for (int i = 0; i < FILTER_SIZE; i++) 

        sum += ir_buffer[i]; 

    return (unsigned int)(sum / FILTER_SIZE); 

} 

// ===================================================== 

// PEAK DETECTION (SIMPLIFIED) 

// ===================================================== 

bool DetectPeak(unsigned int value) 

{ 

    static unsigned int prev = 0; 

    if (value > 50000 && prev <= 50000) 

    { 

        prev = value; 

        return true; 

    } 

    prev = value; 

    return false; 

} 

// ===================================================== 

// TEMPERATURE CONVERSION 

// ===================================================== 

float ConvertTemp(unsigned int raw) 

{ 

    float temp = raw * 0.02; 

    return temp - 273.15; 

} 

// ===================================================== 

// SENSOR STUBS (REPLACE WITH REAL I2C READS) 

// ===================================================== 

unsigned int Read_IR_Value(void) 

{ 

    return 50000 + (ms_ticks % 2000); 

} 

unsigned int Read_Temp_Raw(void) 

{ 

    return 3000 + (ms_ticks % 50); 

}